或許有人會想說我這兩天一直在鐵人賽跟離職的學姊示好,就不怕被公司的人看到嗎?哎這我還真不怕,畢竟咱公司紀律嚴謹,上班時間逛不了論壇,下了班就該好好休息哪還看我的廢文,沒在怕ㄏ
所以開始遍歷template吧。在/src/ast/parse.ts的parse方法體加上以下代碼:
while (index < template.length - 1) {
const rest = template.substring(index);
// 檢測剩餘文字「開頭」為開始標籤、結束標籤或內文
if (startRegExp.test(rest)) {
} else if (endRegExp.test(rest)) {
} else if (wordRegExp.test(rest)) {
} else {
index++;
}
}
很簡單的邏輯,遍歷template的每個字,看以那個字開頭的是上標籤、下標籤還是內文或啥也不是。除了rest是剩餘的英文以外,其他都沒甚麼好說的。
還記得昨天的流程嗎?不記得就複習一下:
當startRegExp.test(rest)為true時,代表我們遍歷到了上標籤,這時就要給tagStack和textStack入棧。
if (startRegExp.test(rest)) {
// 捕獲標籤內文字賦值tag
const tag = rest.match(startRegExp)[1];
const attrsStr = rest.match(startRegExp)[2];
const attrs = parseAttrsString(attrsStr);
tagStack.push(tag);
textStack.push({ tag, children: [], attrs });
// 標籤文字字數 + 2即為開始標籤字數,將指針後移至開始標籤之後
index += tag.length + 2 + (attrsStr ? attrsStr.length : 0);
}
parseAttrsString之後再說,先假裝這裡有個能把標籤屬性從字串解析成對象的函數。所以這邊做的事就是把標籤名和屬性解析出來,然後入棧。
至於最下面那段:
index += tag.length + 2 + (attrsStr ? attrsStr.length : 0);
把上標籤都遍歷完了,就把指針移動到上標籤的後面,之所以+2是因為上標籤除了裡面的文字與空格,外面還有兩個尖括號。
接著討論endRegExp.test(rest)為true,也就是匹配到下標籤的情況。
else if (endRegExp.test(rest)) {
const tag = rest.match(endRegExp)[1];
if (tag === tagStack[tagStack.length - 1]) {
tagStack.pop();
const popText = textStack.pop();
textStack[textStack.length - 1].children.push(popText);
} else {
throw new Error(tagStack[tagStack.length - 1] + '標籤未封閉!');
}
index += tag.length + 3;
}
看完下標籤,就代表當前這個棧頂的標籤已經解析完成,就能讓兩個棧都出棧,並把解析完的textStack出棧的項push進新棧頂的children屬性。
最下面index之所以是+3是因為下標籤有'</'和'>'。
接著看wordRegExp.test(rest)為true,也就是匹配到內文時。
else if (wordRegExp.test(rest)) {
// 剩餘文字開頭非標籤,結尾為下標籤,意即標籤內容
const text = rest.match(wordRegExp)[1];
// 檢查標籤內文字是否全為空
if (!/^\s+$/.test(text)) {
textStack[textStack.length - 1].children = parseTextData(text);
}
index += text.length;
}
parseTextData同樣先不管它,就當作它是一個能解析內文,把標籤中的內容變成純文字的方法,之後再回來補。
如此一來解析內文的部分就單純了許多,把內文賦值給textStack棧頂的children屬性,然後讓指針移至內文後面。
順帶一提,這邊可以不必擔心內文會不會包含更多html子標籤,因為如果有子標籤,就會被前面的if(startRegExp.test(rest))
判斷攔截,能進到else if (wordRegExp.test(rest))
就代表解析的過程不會遇到更多子標籤了。
最後迴圈解析template,看到上標籤就解析其屬性,給tagStack和textStack入棧;看到下標籤就雙棧出棧,把textStack出棧項推入textStack棧頂的children;看到內文就解析內文,賦值給textStack棧頂的children……不斷重複。
然後就能完成這個流程圖:
大致上的邏輯就是這樣,接下來就剩下如何解析屬性以及內文,明天我們將著手開始進行這兩項解析。
github - [Day 25]ast抽象語法樹 - 2——解析標籤